#include <fmt/format.h>

#include "appcoin2/support/feed/py_market_data_client.h"
#include "coin/proto/coin_request.pb.h"
#include "coin2/base/log.h"

namespace py = pybind11;

namespace appcoin2::support::feed {

using coin2::exchange::base::feed::rest::MarketDataClient;

PyMarketDataClient::PyMarketDataClient() {
  client_.reset(new ::coin2::exchange::base::feed::rest::MarketDataClient());
}

py::bytes PyMarketDataClient::QueryKlines(const std::string& req) {
  coin::proto::KlineRequestProto req_proto;
  bool ret = req_proto.ParseFromString(req);
  CHECK_THROW(ret);
  std::string mea = fmt::format(
      "{}.{}.{}",
      req_proto.market_type(), req_proto.exchange(), req_proto.api_version());
  const google::protobuf::EnumDescriptor* descriptor =
      fastfeed::proto::KlineInterval_descriptor();
  auto value_descriptor = descriptor->FindValueByName(req_proto.kline_period());
  CHECK_THROW(value_descriptor);
  auto kline_interval =
      static_cast<fastfeed::proto::KlineInterval>(value_descriptor->number());
  auto kline = client_->QueryKlines(
      mea, req_proto.symbol(), kline_interval,
      req_proto.start_timestamp(), req_proto.end_timestamp());
  std::string dst;
  ret = kline.SerializeToString(&dst);
  CHECK_THROW(ret);
  return py::bytes(dst);
}

template <typename TradingDataQueryFun>
py::bytes PyMarketDataClient::QueryTradingData(
    TradingDataQueryFun query_ptr,
    const std::string& req) {
  coin::proto::TradingDataRequestProto req_proto;
  bool ret = req_proto.ParseFromString(req);
  CHECK_THROW(ret);
  std::string mea = fmt::format(
      "{}.{}.{}",
      req_proto.market_type(), req_proto.exchange(), req_proto.api_version());
  const google::protobuf::EnumDescriptor* descriptor =
      fastfeed::proto::TradingDataInterval_descriptor();
  auto value_descriptor = descriptor->FindValueByName(req_proto.period());
  CHECK_THROW(value_descriptor);
  auto interval =
      static_cast<fastfeed::proto::TradingDataInterval>(value_descriptor->number());
  auto trading_data = ((*client_).*query_ptr)(
      mea, req_proto.symbol(), interval,
      req_proto.start_timestamp(), req_proto.end_timestamp());
  std::string dst;
  ret = trading_data.SerializeToString(&dst);
  CHECK_THROW(ret);
  return py::bytes(dst);
}

pybind11::bytes PyMarketDataClient::QueryTopLongShortAccountRatio(const std::string& req) {
  return QueryTradingData(
      &MarketDataClient::QueryTopLongShortAccountRatio, req);
}

pybind11::bytes PyMarketDataClient::QueryTopLongShortPositionRatio(const std::string& req) {
  return QueryTradingData(
      &MarketDataClient::QueryTopLongShortPositionRatio, req);
}

pybind11::bytes PyMarketDataClient::QueryGlobalLongShortAccountRatio(const std::string& req) {
  return QueryTradingData(
      &MarketDataClient::QueryGlobalLongShortAccountRatio, req);
}

pybind11::bytes PyMarketDataClient::QueryTakerBuySellRatio(const std::string& req) {
  return QueryTradingData(
      &MarketDataClient::QueryTakerBuySellRatio, req);
}

pybind11::bytes PyMarketDataClient::QueryOpenInterestHist(const std::string& req) {
  return QueryTradingData(
      &MarketDataClient::QueryOpenInterestHist, req);
}

void AddPyMarketDataClient(py::module* m) {
  py::class_<PyMarketDataClient>(*m, "PyMarketDataClient")
      .def(py::init<>())
      .def("query_klines", &PyMarketDataClient::QueryKlines)
      .def("query_top_long_short_account_ratio", &PyMarketDataClient::QueryTopLongShortAccountRatio)
      .def("query_top_long_short_position_ratio", &PyMarketDataClient::QueryTopLongShortPositionRatio)
      .def("query_global_long_short_account_ratio", &PyMarketDataClient::QueryGlobalLongShortAccountRatio)
      .def("query_taker_bus_sell_ratio", &PyMarketDataClient::QueryTakerBuySellRatio)
      .def("query_open_interest_hist", &PyMarketDataClient::QueryOpenInterestHist);
}

PYBIND11_MODULE(py_market_data_client, m) {
  AddPyMarketDataClient(&m);
};

}  // namespace appcoin2::support::feed
